package etxt2db.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/**
 * Container of useful methods regarding operations in the File System.
 * <p>
 * 
 * @author Gonçalo Simões
 * @author Rui Lageira
 *
 */
public class FileSystemUtils {
	
	/**
	 * Copies a directory recursively to a target location.
	 * <p>
	 * @param sourceLocation	the directory to copy
	 * @param targetLocation	the target directory
	 * @throws IOException		if an input/output error occurs
	 */
	public static void copyDirectory(String sourceLocation, String targetLocation) throws IOException {
		copyDirectory_r(new File(sourceLocation), new File(targetLocation));
	}
	
	/**
	 * Recursive method which copies the whole content of a source directory to a target directory.
	 * <p>
	 * @param sourceLocation	the directory to copy
	 * @param targetLocation	the target directory
	 * @throws IOException		if an input/output error occurs
	 */
	private static void copyDirectory_r(File sourceLocation, File targetLocation) throws IOException {
		
		if (sourceLocation.isDirectory()) {
			if (!targetLocation.exists()) {
				targetLocation.mkdir();
			}

			String[] children = sourceLocation.list();
			for (int i = 0; i < children.length; i++) {
				copyDirectory_r(new File(sourceLocation, children[i]), new File(targetLocation, children[i]));
			}
		} else {

			InputStream in = new FileInputStream(sourceLocation);
			OutputStream out = new FileOutputStream(targetLocation);

			// Copy the bits from instream to outstream
			byte[] buf = new byte[1024];
			int len;
			while ((len = in.read(buf)) > 0) {
				out.write(buf, 0, len);
			}
			in.close();
			out.close();
		}
	}
	
	/**
	 * Replaces each occurrence of a string token in a source file by a string value and writes 
	 * the result to a new destination file.
	 * <p>
	 * @param sourceFile	the source file
	 * @param destFile		the destination file with the tokens replaced by the value
	 * @param token			the token to be replaced
	 * @param replacement	the value to replace the token
	 * @throws IOException	if an input/output error occurs
	 */
	public static void replaceFileToken(String sourceFile, String destFile, String token, String replacement) throws IOException {
        File scrFile = new File(sourceFile);
        BufferedReader reader = new BufferedReader(new FileReader(scrFile));
        String line = "", oldText = "";
        while((line = reader.readLine()) != null)
            {
            oldText += line + "\r\n";
        }
        reader.close();
        
        // replace a word in a file
        String newText = oldText.replace(token, replacement);
       
        FileWriter writer = new FileWriter(destFile);
        writer.write(newText);
        writer.close();
	}
	
	/**
	 * Parses a file and returns a {@link Document} object.
	 * <p>
	 * @param filePath	the path to the file to parse
	 * @return	the {@link Document} object containing the XML file data
	 * @throws ParserConfigurationException	if a {@link DocumentBuilder} cannot be created
	 * @throws SAXException					if a parsing error occurs
	 * @throws IOException					if an input/output error occurs
	 */
	public static Document readDocument(String filePath) throws ParserConfigurationException, SAXException, IOException {
		
		File file = new File(filePath);
		
		return readDocument(file);
	}
	
	/**
	 * Parses an HTML or XML file and returns a {@link Document} object.
	 * <p>
	 * @param file the file to parse
	 * @return	the {@link Document} object containing the file data
	 * @throws ParserConfigurationException	if a {@link DocumentBuilder} object cannot be created
	 * @throws SAXException					if a parsing error occurs
	 * @throws IOException					if an input/output error occurs
	 */
	public static Document readDocument(File file) throws ParserConfigurationException, SAXException, IOException {
		
		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
		
		return documentBuilder.parse(file);
	}
	
	/**
	 * Writes a {@link Document} object data to a file.
	 * <p>
	 * @param document	the {@link Document} object to write
	 * @param filePath	the path to the file where the data is to be written
	 * @throws FileNotFoundException	if a problem opening or creating the file occurs
	 * @throws TransformerException		if a problem with writing process occurs
	 */
	public static void writeDocument(Document document, String filePath) throws FileNotFoundException, TransformerException {
		
		DOMSource source = new DOMSource(document);
		
		PrintStream ps = new PrintStream(filePath);
		StreamResult result = new StreamResult(ps);
		
		TransformerFactory transformerFactory = TransformerFactory.newInstance();
		transformerFactory.setAttribute("indent-number", 10);
		Transformer transformer = transformerFactory.newTransformer();
		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
		
		transformer.transform(source, result);
	}
	
	/**
	 * Writes a {@link Document} object data to a file.
	 * <p>
	 * @param document	the {@link Document} object to write
	 * @param file the file where the data is to be written
	 * @throws FileNotFoundException	if a problem opening or creating the file occurs
	 * @throws TransformerException		if a problem with writing process occurs
	 */
	public static void writeDocument(Document document, File file) throws FileNotFoundException, TransformerException {
		
		DOMSource source = new DOMSource(document);
		
		PrintStream ps = new PrintStream(file);
		StreamResult result = new StreamResult(ps);
		
		TransformerFactory transformerFactory = TransformerFactory.newInstance();
		transformerFactory.setAttribute("indent-number", 10);
		Transformer transformer = transformerFactory.newTransformer();
		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
		
		transformer.transform(source, result);
	}
	
}
